#include "stdafx.h"
#include "Texture2D.h"

#include "Log.h"
#include "Exception.h"
#include "MemUtils.h"
#include "ErrorHandlers.h"

namespace Cvekas {

Texture2D::Texture2D(ResourceId id, D3DDevicePtr device, const std::string& name, const std::string& filename,
	D3DFORMAT format, bool pow_of2, bool lockable, bool gen_mipmaps)
	: 
BaseTexture(id, device, name, format), 
filename(filename)
{	
	width = height = pow_of2 ? D3DX_DEFAULT : D3DX_DEFAULT_NONPOW2;
	mipmaps = gen_mipmaps ? D3DX_DEFAULT : 1;
	usage = 0;
	is_lockable = true;
	is_locked = false;
	is_ready = false;
	is_dynamic = false;
	load();
}

Texture2D::Texture2D(ResourceId id, D3DDevicePtr device, const std::string& name, uint width, 
	uint height, D3DFORMAT format, bool gen_mipmaps, bool dynamic)
	: 
BaseTexture(id, device, name, format), 
filename("")
{	
	if(dynamic && gen_mipmaps)
		throw EXCEPTION("Mipmapped dynamic textures in unsupported");

	this->width = width;
	this->height = height;
	mipmaps = gen_mipmaps ? D3DX_DEFAULT : 1;
	usage = dynamic ? D3DUSAGE_DYNAMIC : 0;
	is_lockable = true;
	is_locked = false;
	is_ready = false;
	is_dynamic = dynamic;
	create();
}

Texture2D::~Texture2D()
{
	// Releasing is handled by BaseTexture
	if(!is_released)
		LOG(FORMAT("Texture %s released", % name));
}

bool Texture2D::isLockable() const
{
	return is_lockable;
}

bool Texture2D::isLocked() const
{
	return is_locked;
}

bool Texture2D::isDynamic() const
{
	return is_dynamic;
}

LockedTexture Texture2D::lock(bool read_only)
{
	if(!is_lockable)
		throw EXCEPTION("Texture is not lockable!");
	if(is_locked)
		throw EXCEPTION("Texture is already locked!");
	
	is_locked = true;
	
	IDirect3DTexture9* temp_texture = static_cast<IDirect3DTexture9*>(texture);
	D3DLOCKED_RECT locked_rect;
	DWORD mode;
	if(is_dynamic)
		mode = D3DLOCK_DISCARD;
	else
		mode = read_only ? D3DLOCK_READONLY : NULL;

	E_E(temp_texture->LockRect(0, &locked_rect, NULL, mode));
	LockedTexture locked;
	locked.pitch = locked_rect.Pitch;
	locked.data = locked_rect.pBits;
	return locked;
}

void Texture2D::unlock()
{
	if(!is_lockable)
		throw EXCEPTION("Texture is not lockable!");
	if(!is_locked)
		throw EXCEPTION("Texture is not locked!");

	IDirect3DTexture9* temp_texture = static_cast<IDirect3DTexture9*>(texture);
	E_E(temp_texture->UnlockRect(0));
	is_locked = false;
}

const std::string& Texture2D::getFilename() const
{
	return filename;
}

void Texture2D::load()
{
	IDirect3DTexture9* temp_texture = static_cast<IDirect3DTexture9*>(texture);

	D3DXIMAGE_INFO img_info;
	E_E(D3DXGetImageInfoFromFile(filename.c_str(), &img_info));
	
	// All non-DDS file formats are loaded as A8R8G8B8
	if(img_info.ImageFileFormat != D3DXIFF_DDS)
		format = D3DFMT_A8R8G8B8;
	
	LOG(FORMAT("Loading texture %s from %s", % name % filename));
	E_E(D3DXCreateTextureFromFileEx(device.get(), filename.c_str(), width, height, mipmaps, usage, format, D3DPOOL_MANAGED, 
		D3DX_DEFAULT, D3DX_DEFAULT, 0, NULL, NULL, &temp_texture ));
	E_E(temp_texture->GetLevelDesc(0, &description));
	
	// We might get other format than we asked for
	format = description.Format;
	
	texture = temp_texture;
	is_ready = true;
	is_released = false;
}

void Texture2D::create()
{
	IDirect3DTexture9* temp_texture = static_cast<IDirect3DTexture9*>(texture);
	
	LOG(FORMAT("Creating texture %s", % name));
	E_E(D3DXCreateTexture(device.get(), width, height, mipmaps, usage, format, 
		(is_dynamic ? D3DPOOL_DEFAULT : D3DPOOL_MANAGED), &temp_texture));
	E_E(temp_texture->GetLevelDesc(0, &description));
	
	// We might get other format than we asked for
	format = description.Format;
	
	texture = temp_texture;
	is_ready = true;
	is_released = false;
}

void Texture2D::onDeviceLost()
{
	// Only dynamic textures must be released on device lost
	if(is_dynamic && !is_released)
	{
		LOG(FORMAT("Releasing dynamic texture %s", % name));
		safeRelease(texture);
		is_released = true;
	}

	is_ready = false;
}

void Texture2D::onDeviceReset()
{
	if(is_dynamic && is_released)
	{
		create();
		is_released = false;
	}

	is_ready = true;
}

} //namespace
